using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace Gko.Utils
{
	/// <summary>
	/// Native SYSTEMTIME structure description
	/// </summary>
	[StructLayout(LayoutKind.Sequential)]
	public struct NativeSystemTime : IEquatable<NativeSystemTime>
	{
		private ushort year;

		/// <summary>
		/// Gets or sets the year.
		/// </summary>
		/// <value>The year.</value>
		public int Year
		{
			get
			{
				return year;
			}
			set
			{
				checked {
					year = (ushort)value;
				}
			}
		}
		private ushort month;

		/// <summary>
		/// Gets or sets the month.
		/// </summary>
		/// <value>The month.</value>
		public int Month
		{
			get
			{
				return month;
			}
			set
			{
				checked {
					month = (ushort)value;
				}
			}
		}
		private ushort dayOfWeek;

		/// <summary>
		/// Gets or sets the day of week.
		/// </summary>
		/// <value>The day of week.</value>
		public DayOfWeek DayOfWeek
		{
			get
			{
				return (DayOfWeek)dayOfWeek;
			}
			set
			{
				checked {
					dayOfWeek = (ushort)value;
				}
			}
		}
		private ushort day;

		/// <summary>
		/// Gets or sets the day.
		/// </summary>
		/// <value>The day.</value>
		public int Day
		{
			get
			{
				return day;
			}
			set
			{
				checked {
					day = (ushort)value;
				}
			}
		}
		private ushort hour;

		/// <summary>
		/// Gets or sets the hour.
		/// </summary>
		/// <value>The hour.</value>
		public int Hour
		{
			get
			{
				return hour;
			}
			set
			{
				checked {
					hour = (ushort)value;
				}
			}
		}
		private ushort minute;

		/// <summary>
		/// Gets or sets the minute.
		/// </summary>
		/// <value>The minute.</value>
		public int Minute
		{
			get
			{
				return minute;
			}
			set
			{
				checked {
					minute = (ushort)value;
				}
			}
		}
		private ushort second;

		/// <summary>
		/// Gets or sets the second.
		/// </summary>
		/// <value>The second.</value>
		public int Second
		{
			get
			{
				return second;
			}
			set
			{
				checked {
					second = (ushort)value;
				}
			}
		}
		private ushort milliseconds;

		/// <summary>
		/// Gets or sets the milliseconds.
		/// </summary>
		/// <value>The milliseconds.</value>
		public int Milliseconds
		{
			get
			{
				return milliseconds;
			}
			set
			{
				checked {
					milliseconds = (ushort)value;
				}
			}
		}

		/// <summary>
		/// Performs an explicit conversion from <see cref="T:System.DateTime"/> to <see cref="T:Gko.Utils.NativeSystemTime"/>.
		/// </summary>
		/// <param name="time">The time.</param>
		/// <returns>The result of the conversion.</returns>
		public static explicit operator NativeSystemTime(DateTime time)
		{
			return new NativeSystemTime {
				Year = time.Year,
				Month = time.Month,
				DayOfWeek = time.DayOfWeek,
				Day = time.Day,
				Hour = time.Hour,
				Minute = time.Minute,
				Second = time.Second,
				Milliseconds = time.Millisecond,
			};
		}

		/// <summary>
		/// Performs an explicit conversion from <see cref="T:Gko.Utils.NativeSystemTime"/> to <see cref="T:System.DateTime"/>.
		/// </summary>
		/// <param name="time">The time.</param>
		/// <returns>The result of the conversion.</returns>
		public static explicit operator DateTime(NativeSystemTime time)
		{
			return new DateTime(time.year, time.month, time.day, time.hour, time.minute, time.second, time.milliseconds);
		}

		/// <summary>
		/// Converts this instance to <see cref="T:System.DateTime"/>.
		/// </summary>
		/// <returns>The result of the conversion.</returns>
		public DateTime ToDateTime()
		{
			return (DateTime)this;
		}

		/// <summary>
		/// Tries set current date and time that structure holds as system time.
		/// </summary>
		public bool TrySetAsSystemTime()
		{
			var time = this;
			return NativeMethods.SetSystemTime(ref time) != 0;
		}

		/// <summary>
		/// Sets current date and time that structure holds as system time.
		/// </summary>
		/// <exception cref="Exception"><c>Exception</c>.</exception>
		public void SetAsSystemTime()
		{
			if (this.TrySetAsSystemTime()) {
				return;
			}
			var hResult = Marshal.GetHRForLastWin32Error();
			throw Marshal.GetExceptionForHR(hResult);
		}

		#region IEquatable<NativeSystemTime> Members

		/// <summary>
		/// Indicates whether the current object is equal to another object of the same type.
		/// </summary>
		/// <param name="other">An object to compare with this object.</param>
		/// <returns>
		/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
		/// </returns>
		public bool Equals(NativeSystemTime other)
		{
			return ((DateTime)this).Equals((DateTime)other);
		}

		#endregion

		/// <summary>
		/// Indicates whether this instance and a specified object are equal.
		/// </summary>
		/// <param name="obj">Another object to compare to.</param>
		/// <returns>
		/// true if <paramref name="obj"/> and this instance are the same type and represent the same value; otherwise, false.
		/// </returns>
		public override bool Equals(object obj)
		{
			if (ReferenceEquals(obj, null)) {
				return false;
			}
			if (obj is NativeSystemTime) {
				return this.Equals((NativeSystemTime)obj);
			}
			return false;
		}

		/// <summary>
		/// Returns the hash code for this instance.
		/// </summary>
		/// <returns>
		/// A 32-bit signed integer that is the hash code for this instance.
		/// </returns>
		public override int GetHashCode()
		{
			return ((DateTime)this).GetHashCode();
		}

		/// <summary>
		/// Implements the operator ==.
		/// </summary>
		/// <param name="one">The one.</param>
		/// <param name="another">Another.</param>
		/// <returns>The result of the operator.</returns>
		public static bool operator ==(NativeSystemTime one, NativeSystemTime another)
		{
			return one.Equals(another);
		}

		/// <summary>
		/// Implements the operator !=.
		/// </summary>
		/// <param name="one">The one.</param>
		/// <param name="another">Another.</param>
		/// <returns>The result of the operator.</returns>
		public static bool operator !=(NativeSystemTime one, NativeSystemTime another)
		{
			return !one.Equals(another);
		}

		/// <summary>
		/// Returns the fully qualified type name of this instance.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.String"/> containing a fully qualified type name.
		/// </returns>
		public override string ToString()
		{
			return ((DateTime)this).ToString();
		}
	}
}